package graph.editor;

import graph.util.Shapes;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RectangularShape;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.event.MouseInputListener;

import automaton.State;
import automaton.Transition;

public class GraphComponent extends JComponent implements MouseInputListener,
		KeyListener, Observer {

	private final int ARR_SIZE = 4;
	private static int n = 0;
	int n1 = 0;
	List<Vertex> vertices = new ArrayList<Vertex>();
	Vertex currentVertex = null;
	Vertex IniVertex = null;
	IniVertex ini;
	boolean iniState = false;
	Graphics2D g2;
	private RectangularShape vertexSample = new Ellipse2D.Double(0, 0, 10, 10);

	private RectangularShape currentJointPoint = null;

	List<Edge> edges = new ArrayList<Edge>();
	Edge currentEdge = null;

	Edge IniEdge, FinEdge;

	public GraphComponent() {
		addMouseListener(this);
		addMouseMotionListener(this);
		addKeyListener(this);
	}

	protected void paintComponent(Graphics g) {
		if (isOpaque()) {
			g.setColor(getBackground());
			g.fillRect(0, 0, getWidth(), getHeight());
		}
		// set color to shape and line
		g.setColor(getForeground());
		g2 = (Graphics2D) g;
		for (Vertex v : vertices)
			v.draw(g2);
		for (Edge e : edges)
			e.draw(g2);
	}

	private Vertex getVertex(int x, int y) {
		for (Vertex v : vertices) {
			if (v.shape.contains(x, y))
				return v;
		}
		return null;
	}

	private RectangularShape getJointPoint(int x, int y) {
		for (Edge e : edges) {
			RectangularShape jp = e.getJointPoint(x, y);
			if (jp != null)
				return jp;
		}
		return null;
	}

	private static final double EDGE_EPSILON = 2.0;

	private Edge getEdge(int x, int y) {
		for (Edge e : edges)
			if (e.contains(x, y, EDGE_EPSILON))
				return e;
		return null;
	}

	public void setVertexSample(RectangularShape sample) {
		vertexSample = sample;
	}

	private RectangularShape newVertexShape(int x, int y) {
		RectangularShape rs = (RectangularShape) vertexSample.clone();
		Shapes.moveShape(rs, x, y);
		return rs;
	}

	private Vertex createVertex(int x, int y) {
		RectangularShape rs = newVertexShape(x, y);
		Vertex v = new Vertex(rs, Integer.toString(n++), getForeground(),false, false);
		vertices.add(v);
		return v;
	}

	private IniVertex creatIniVertex(int x, int y) {
		RectangularShape rs = newVertexShape(x, y);
		IniVertex v = new IniVertex(rs, Integer.toString(n++), getForeground(),true,
				false);
		vertices.add(v);
		return v;
	}

	private void removeVertex(Vertex v) {
		List<Edge> toRemove = new ArrayList<Edge>();
		for (Edge e : edges) {
			if (e.v1 == v || e.v2 == v)
				toRemove.add(e);
		}
		for (Edge e : toRemove)
			removeEdge(e);
		vertices.remove(v);
	}

	private void removeEdge(Edge e) {
		remove(e.textField);
		edges.remove(e);
	}

	private Edge startEdge(Vertex v) {
		RectangularShape rs2 = newVertexShape(0, 0);
		RectangularShape rs = v.shape;
		rs2.setFrameFromCenter((int) rs.getCenterX(), (int) rs.getCenterY(),
				(int) rs.getCenterX(), (int) rs.getCenterY());
		Edge l = new Edge(v, new Vertex(rs2, null, getForeground(),false, false));
		edges.add(l);
		return l;
	}

	private void addJointPoint(Edge e) {
		e.addJointPoint();
	}

	private void endLine(Edge e, int x, int y) {
		Vertex v = getVertex(x, y);
		if (v == null) {
			e.v2.shape.setFrameFromCenter(x, y, x + vertexSample.getHeight()
					/ 2, y + vertexSample.getWidth() / 2);
			e.v2.label = Integer.toString(n++);
			vertices.add(e.v2);
		} else
			e.v2 = v;
	}

	public void mousePressed(MouseEvent e) {
		requestFocusInWindow();

		if ((e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
			return;
		int x = e.getX();
		int y = e.getY();
		Vertex v = getVertex(x, y);
		// Khoi tao Vertex bat dau
		if (v == null && n1 == 0) {
			currentJointPoint = getJointPoint(x, y);
			
			v = creatIniVertex(x, y);
			iniState=true;
			n1++;
		}
		if (v == null && currentJointPoint == null) {
			v = createVertex(x, y);
			n1++;
		}
		if (v != null && e.isAltDown()) {
			currentEdge = startEdge(v);
			n1++;
		} else {
			currentVertex = v;
			repaint();
		}
	}

	public void mouseDragged(MouseEvent e) {
		if (currentVertex != null) {
			Shapes.moveShape(currentVertex.shape, e.getX(), e.getY());
			repaint();
		} else if (currentEdge != null) {
			Shapes.moveShape(currentEdge.v2.shape, e.getX(), e.getY());
			repaint();
		} else if (currentJointPoint != null) {
			Shapes.moveShape(currentJointPoint, e.getX(), e.getY());
			repaint();
		}
	}

	public void addEdgeLabel(final Edge e) {
		final JTextField textField = new JTextField();
		textField.setSize(70, 25);
		textField.setLocation(e.labelPosition());
		e.textField = textField;
		this.add(textField);
	}

	public void loadLabel(final Edge e) {
		JTextField textField = new JTextField();
		textField.setSize(70, 25);
		textField.setLocation(e.labelPosition());
		textField = e.textField;
		this.add(textField);
	}

	public void mouseReleased(MouseEvent e) {
		if (currentEdge != null) {
			endLine(currentEdge, e.getX(), e.getY());
			addEdgeLabel(currentEdge);
			currentEdge = null;
			repaint();
		}
		if (IniEdge != null) {
			endLine(IniEdge, e.getX(), e.getY());
			IniEdge = null;
			repaint();
		}
		currentVertex = null;
		currentJointPoint = null;
	}

	public void mouseMoved(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mouseClicked(MouseEvent e) {
		if (e.getButton() == MouseEvent.BUTTON3) {
			int x = e.getX();
			int y = e.getY();
			Vertex v = getVertex(x, y);
			if (v != null) {
				removeVertex(v);
				repaint();
				return;
			}
			for (Edge edge : edges) {
				RectangularShape jp = edge.getJointPoint(x, y);
				if (jp != null) {
					edge.removeJointPoint(jp);
					repaint();
					return;
				}
			}
			Edge edge = getEdge(x, y);
			if (edge != null) {
				removeEdge(edge);
				repaint();
				return;
			}
		}
		if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
			int x = e.getX();
			int y = e.getY();
			Vertex v = getVertex(x, y);
			if (iniState == true) {
				if (vertices.get(0).shape.getX() == v.shape.getX()
						&& vertices.get(0).shape.getY() == v.shape.getY()) {
					vertices.get(0).fin = true;
					ini.fin = true;
				}
				v.fin = true; // thay doi state cua vertex
				repaint();
			}
		}
	}

	public void keyPressed(KeyEvent e) {
		if (e.getKeyCode() == ' ' && currentEdge != null) {
			addJointPoint(currentEdge);
		}
	}

	public void keyReleased(KeyEvent e) {
	}

	public void keyTyped(KeyEvent e) {
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		// TODO Auto-generated method stub
		Transition<String> a = (Transition<String>) arg1;
		// NewState src = (NewState) a.source();
		NewState tar = (NewState) a.target();

		paintState(tar);

	}

	public void paintState(State state) {

		try {
			NewState tar = (NewState) state;
			tar.getnVertex().setColor(Color.BLUE);
			repaint();
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}
